在 PawPal 開發到後期時,前端部署到 Vercel 這一塊主要是由我負責。
一開始我對「部署」這件事的想像其實很簡單。
網站在自己的電腦可以正常開、功能可以跑,接下來應該就是:
把專案放到 Vercel
↓
Build
↓
網址打開
↓
完成
但真的開始處理部署之後,我才發現:
本機可以正常執行,跟正式環境可以正常執行,其實是兩件不同的事。
有些問題,在自己的電腦上甚至完全看不出來。
一定要真的把網站放到正式環境,才會知道還有哪些地方沒有處理好。
其中一個讓我很有印象的問題,是 PawPal 部署到 Vercel 之後遇到的頁面 404。
我們在本機開發時,從首頁切到 Dashboard、行事曆或其他頁面都很正常。
但部署之後卻發現:
如果直接進入某些網址,或是在那個頁面按重新整理,就可能直接看到:
404 Not Found
我當時很疑惑。
因為 Vue Router 明明有這個頁面。
為什麼從網站裡面點進去可以,重新整理之後卻不行?
後來團隊一起排查,我才慢慢理解:
Vue Router 知道 /dashboard 是哪個頁面,不代表部署平台一開始也知道。
PawPal 使用 Vue Router 的 history mode。
網站已經載入之後,可以先用「簡化概念」理解:
使用者切換頁面
↓
Vue Router 判斷網址
↓
顯示對應畫面
但如果直接開:
/dashboard
或是在這個頁面重新整理,瀏覽器會先把這個網址交給部署平台。
如果部署平台不知道這個路徑應該交給 Vue App 處理,就可能直接回傳 404。
後來團隊透過 Vercel 的 rewrite 設定來處理這個問題:
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}
可以用「簡化概念」理解成:
使用者直接進入某個網址
↓
Vercel 先回傳 index.html
↓
Vue App 啟動
↓
Vue Router 再判斷要顯示哪個頁面
這個問題不是我一個人解決的,而是團隊一起遇到、一起排查。
但也是從這次開始,我第一次很明顯感受到:
本機開發環境其實默默幫我們處理了很多事情,到了正式環境,就要重新確認這些設定。
除了 Router,部署過程中另一個讓我卡很久的,就是環境變數。
PawPal 開發到後面,不同功能陸續需要不同的環境設定。
前端像是會使用:
VITE_API_BASE_URL
VITE_GOOGLE_CLIENT_ID
VITE_LINE_CHANNEL_ID
當組員做的新功能需要新增或修改環境變數時,通常也會再來找我,到 Vercel 裡調整正式環境的設定。
但老實說,一開始的我比較像是:
組員:
這個功能要加一個環境變數
我:
好,我去 Vercel 設
我知道自己「要做什麼」。
可是那時候還沒有真的理解:
為什麼本機
.env已經有了,Vercel 還要再設定一次?
現在回頭看,我覺得當時的自己其實是:
會設定,但還沒有真的懂環境變數。
我照著需求設定了好幾次之後,才慢慢搞懂:
不是「.env 有寫就好」,而是每個執行環境都有自己的設定。
.env,不代表 Vercel 也有以前我會很自然地把 .env 想成:
專案的環境變數設定檔。
後來才發現,還要多問一件事:
這是哪一個環境的設定?
本機開發時,可以用「簡化概念」理解成:
Source Code
↓
本機 .env
↓
Vite Dev Server
↓
網站
部署時則會變成:
Source Code
↓
Vercel Environment Variables
↓
vite build
↓
正式網站
也就是說:
我的電腦
≠
Vercel
我電腦裡有的 .env,Vercel 並不會因此自動知道裡面的設定。
所以很容易出現:
本機有設定
↓
功能正常
但正式環境少了同一個設定後:
Vercel 沒有對應設定
↓
部署後功能出問題
這時候問題甚至不一定是程式寫錯。
可能只是正式環境少了它需要的設定。
以前開發時,我最常使用:
npm run dev
所以很容易把眼前看到的網站當成:
最後部署出去大概也是這樣。
但正式部署前,PawPal 還會經過:
npm run build
背後執行的是:
vite build
部署時,前端 build 會用到的環境變數也必須存在於 Vercel 的部署環境中,Vite 才能用正確的設定產生正式版本。
可以用「簡化概念」理解:
Source Code
+
部署環境設定
↓
vite build
↓
產生部署用檔案
↓
Vercel
也是到了這個階段,我才把原本分開理解的:
.env
build
部署
三件事情慢慢連在一起。
.env 裡,不代表它就是秘密環境變數還有一個我後來才注意到的地方。
PawPal 前端使用 Vite,會讀取像:
VITE_API_BASE_URL
這類 VITE_* 變數。
在 Vite 裡,VITE_* 變數會暴露給前端程式碼使用,並進入前端建置結果。
所以不能因為它寫在 .env 裡,就覺得:
這個值一定是安全的。
像 API Base URL、Client ID 這類本來就要提供給前端使用的資訊,可以作為前端環境設定。
但真正需要保密的東西,例如:
資料庫密碼
JWT Secret
Server-side Secret
就不能放進前端可以讀取的 VITE_* 變數。
這時候我才比較理解:
.env 是用來管理不同環境的設定,不是把資料藏起來的魔法。
如果現在重新做一次類似的專案,我不會再因為本機所有功能都可以跑,就直接覺得:
應該可以上線了。
第一個我會確認的是 Router。
如果使用 SPA 和 history mode,我會先想到:
使用者直接進入某個 route,或重新整理時,正式環境能不能正確處理?
第二個是本機和正式環境的差異。
我會開始去確認:
本機正常
≠
正式環境一定正常
像是 build、部署平台和相關設定。
第三個則是環境變數。
如果後來增加一個新的 env,我會同時確認:
本機需要更新嗎?
↓
Vercel 需要更新嗎?
而不是只修改其中一邊。
但我也不會覺得專案一開始就能把所有環境變數全部列出來。
因為很多設定,本來就是功能做到後面才陸續出現。
真正重要的是:
每次新的環境需求出現時,知道自己還需要確認哪些地方。
以前寫功能時,我最常注意的是:
畫面有沒有正常?
API 有沒有回資料?
功能有沒有跑?
開始負責部署之後,我看程式的方式也變了。
以前只會問:
功能有沒有跑?
現在還會多問一句:
它現在是在哪個環境跑?
因為到了另一個環境,Router、環境變數、build 和部署平台,都可能帶來新的問題。
部署不是把網站從 localhost 搬到網路上,而是讓同一份程式開始面對另一個環境。
Router、環境變數、build 和部署設定慢慢處理完之後,PawPal 終於越來越接近真正上線。
下一步,就是讓網站真的有一個大家都能打開的正式網址。
而當我第一次看到 PawPal 真正在線上跑起來時,那個感覺和看著 localhost 完全不一樣。
下一篇:
Day 24|部署成功的那一刻,我差點哭出來。